package com.lmmob.autotest.uitest.controls;

import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.lang.StringUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;

import java.util.*;

public class Grid extends CompositeControl {
    private String query;
    private String title;
    private String id = StringUtils.EMPTY;
    private PagingToolbar pagingToolbar;

    public Grid(WebDriver webDriver) {
        super(webDriver);
        addPagingToolbar(webDriver);
        this.query = ".query(\"grid\")[0]";
    }

    public Grid(String title, WebDriver webDriver) {
        super(webDriver);
        addPagingToolbar(webDriver);
        this.title = title;
        this.query = String.format(".query(\"grid[title='%s']\")[0]", title);
    }

    private void addPagingToolbar(WebDriver webDriver) {
        this.pagingToolbar = new PagingToolbar(webDriver);
        this.addChild(pagingToolbar);
    }

    @Override
    protected String getSelfQuery() {
        return this.query;
    }

    @Override
    protected String getName(){
        return this.title;
    }

    @Override
    public String getId() {
        if (StringUtils.isNotBlank(id)) {
            return id;
        }
        if (StringUtils.isNotBlank(title)) {
            try {
                id = (String) runScript("return " + this.getQuery() + ".id");
            } catch (Exception e) {
                Long len = (Long) runScript("return " + getParent().getQuery() + ".query(\"grid\").length");
                for (int i = 0; i < len; ++i) {
                    String actualTitle = (String) runScript("return " + getParent().getQuery() + String.format(".query(\"grid\")[%s].title", i));
                    if (StringUtils.isNotBlank(actualTitle) && actualTitle.contains(this.title)) {
                        id = (String) runScript("return " + getParent().getQuery() + String.format(".query(\"grid\")[%s].id", i));
                    }
                }
            }
        } else {
            id = super.getId();
        }
        return id;
    }

    protected final String getQueryById() {
        return String.format("Ext.ComponentQuery.query(\"[id='%s']\")[0]", getId());
    }

    public WebElement getElement(String text) {
        WebElement gridElement = getGridElement();
        return gridElement.findElement(By.xpath(String.format("//div[text()='%s']", text)));
    }

    private WebElement getGridElement() {
        return findElement(By.id(getId()));
    }

    private Map<String, Integer> map = new HashMap<String, Integer>();

    protected Integer mapColumnTextToIndex(String name) {
        if (map.isEmpty()) {
            Long len = (Long) runScript("return " + this.getQueryById() + ".columns.length");
            for (int i = 0; i < len; ++i) {
                String text = (String) runScript(String.format("return " + this.getQueryById() + ".columns[%s].text", i));
                map.put(text, i);
            }
        }
        return map.get(name);
    }

    private Map<String, String> getColumnHeaders() {
        Long len = (Long) runScript("return " + this.getQueryById() + ".columns.length");
        Map<String, String> result = new HashMap<String, String>();
        for (int i = 0; i < len; ++i) {
            String text = (String) runScript(String.format("return " + this.getQueryById() + ".columns[%s].text", i));
            String dataIndex = (String) runScript(String.format("return " + this.getQueryById() + ".columns[%s].dataIndex", i));
            result.put(dataIndex, text);
        }
        return result;
    }

    public List<List<String>> getCurrentPage() {
        List<WebElement> rowElements = getRows();
        List<List<String>> rows = new ArrayList<List<String>>();
        for (int i = 1; i < rowElements.size(); i++) {
            WebElement row = rowElements.get(i);
            List<String> columns = new ArrayList<String>();
            List<WebElement> columnElements = row.findElements(By.tagName("td"));
            if (columnElements.size() == 0) continue;
            for (WebElement columnElement : columnElements) {
                String text = columnElement.getText();
                if (StringUtils.isNotBlank(text))
                    columns.add(text);
            }
            rows.add(columns);
        }
        return rows;
    }

    public List<WebElement> searchRow(Integer searchColumn, String keyword) {
        List<WebElement> currentPage = Collections.EMPTY_LIST;
        while (isNotSearched(currentPage)) {
            currentPage = searchInCurrentPage(searchColumn, keyword);
            if (isSearched(currentPage)) break;
        }
        return currentPage;
    }

    private boolean isSearched(List<WebElement> currentPage) {
        return (!currentPage.isEmpty()) || (!pagingToolbar.nextPage());
    }

    private boolean isNotSearched(List<WebElement> currentPage) {
        return currentPage.isEmpty();
    }

    private List<WebElement> searchInCurrentPage(Integer searchColumn, String keyword) {
        List<WebElement> rowElements = getRows();
        for (int n = 1; n < rowElements.size(); n++) {
            WebElement row = rowElements.get(n);
            List<WebElement> columnElements = row.findElements(By.tagName("td"));
            if (columnElements.size() == 0) continue;
            for (Integer i = 0; i < columnElements.size(); i++) {
                String text = columnElements.get(i).getText();
                if (searchColumn.equals(i) && StringUtils.isNotBlank(text) && text.equals(keyword)) {
                    return columnElements;
                }
            }
        }
        return Collections.EMPTY_LIST;
    }

    private List<WebElement> getRows() {
        String bodyId = (String) runScript("return " + getQueryById() + ".body.id");
        WebElement element = findElement(By.id(bodyId));
        return element.findElement(By.tagName("table")).findElements(By.tagName("tr"));
    }

    public List<String> search(String name, String keyword) {
        Integer index = mapColumnTextToIndex(name);
        return search(index, keyword);
    }

    public List<String> search(Integer searchColumn, String keyword) {
        List<WebElement> row = searchRow(searchColumn, keyword);
        List<String> result = new ArrayList<String>();
        for (WebElement column : row) {
            result.add(column.getText());
        }
        return result;
    }

    @Deprecated
    public List<Map<String, String>> getData() {
        Long len = (Long) runScript("return " + this.getQueryById() + ".store.data.items.length");
        List<Map<String, String>> result = new ArrayList<Map<String, String>>();
        Map<String, String> columnHeaders = getColumnHeaders();
        for (int i = 0; i < len; ++i) {
            Map<String, String> data = (Map<String, String>) runScript(String.format("return " + this.getQueryById() + ".store.data.items[%s].data", i));
            HashMap<String, String> temp = new HashMap<String, String>();
            for (String key : data.keySet()) {
                temp.put(getKey(columnHeaders, key), ConvertUtils.convert(data.get(key)));
            }
            result.add(temp);
        }
        return result;
    }

    private String getKey(Map<String, String> columnHeaders, String key) {
        try {
            String textKey = columnHeaders.get(key);
            if (StringUtils.isNotBlank(textKey))
                return textKey;
            else
                return key;
        } catch (Exception e) {
            return null;
        }
    }

    public void nextPage() {
        this.pagingToolbar.nextPage();
    }

    public void prevPage() {
        this.pagingToolbar.prevPage();
    }

    public void firstPage() {
        this.pagingToolbar.firstPage();
    }

    public void lastPage() {
        this.pagingToolbar.lastPage();
    }

}
